/**
 * @file Matrix.h
 * @brief 设计矩阵类Matrix
 * 
 * 在文件中实现了对矩阵的存储方式和基本运算,包括矩阵的加减法、数乘及乘法运算.
 * 
 * @author 叶陈昊
 * @date 2023/10/6
 */

#ifndef MATRIX_H_
#define MATRIX_H_
#include<iostream>

/*定义矩阵类*/
class Matrix
{
private:
    int row;
    int column;
    double** entry;

public:
    /**
     * @brief 默认构造函数
     * 
     * 将行、列赋初值0,指针赋为空指针.
     */
    Matrix(){
        row = 0;
        column = 0;
        entry = nullptr;
    }

    /**
     * @brief 重载构造函数(读入行、列)
     * 
     * 读入矩阵的行和列,并动态分配一个二维数组,其内元素赋初值0.
     */
    Matrix(int _row, int _column){
        row = _row;
        column = _column;
        entry = new double*[row];
        for(int i = 0; i < row; i++)
        {
	        entry[i] = new double[column];
            for(int j = 0; j < column; j++){
                entry[i][j] = 0.0;
            }
        }
    }

    /**
     * @brief 复制构造函数
     * 
     * 读入一整个矩阵,并动态分配一个二维数组,其内元素与读入矩阵的元素一致.
     */
    Matrix(const Matrix &other){
        row = other.row;
        column = other.column;
        entry = new double*[row];
        for(int i = 0; i < row; i++)
        {
	        entry[i] = new double[column];
            for(int j = 0; j < column; j++){
                entry[i][j] = other.entry[i][j];
            }
        }
    }

    /**
     * @brief 析构函数
     * 
     * 将手动new出的指针delete掉
     */
    ~Matrix(){        
        for(int i = 0; i < row; i++)
        {
            if(entry[i] != nullptr){
                delete [] entry[i];
                entry[i] = nullptr;
            }	        
        }
        if(entry != nullptr){
            delete [] entry;
            entry = nullptr;
        }       
    }

    /**
     * @brief 设置矩阵元素
     * 
     * 具体方式:在矩阵的第i行第j列设置元素entry.
     */
    void set_entry(int _row, int _column, double _entry){
        entry[_row][_column] = _entry;
    }

    /**
     * @brief 输入矩阵元素
     * 
     * 依次输入row行column列的元素并赋给矩阵.
     */
    void readin_matrix(){
        for(int i = 0; i < row; i++){
            for(int j = 0; j < column; j++){
                double _entry;
                std::cin >> _entry;
                set_entry(i, j, _entry);
            }
        }
    }

    /**
     * @brief 打印矩阵
     * 
     * 依次输出矩阵row行column列的元素.
     */
    void print_matrix(){
        for(int i = 0; i < row; i++){
            for(int j = 0; j < column; j++){
                std::cout << entry[i][j] << " ";
            }
            std::cout << std::endl;
        }
    }

    /**
     * @brief 等号重载
     */
    Matrix& operator=(const Matrix &other){
        if(this != &other){        
            for(int i = 0; i < row; i++)
            {
                if(entry[i] != nullptr){
                    delete [] entry[i];
                    entry[i] = nullptr;
                }	        
            }
            if(entry != nullptr){
                delete [] entry;
                entry = nullptr;
            }

            row = other.row;
            column = other.column;
            entry = new double*[row];
            for(int i = 0; i < row; i++)
            {   
	            entry[i] = new double[column];
                for(int j = 0; j < column; j++){
                    entry[i][j] = other.entry[i][j];
                }
            }
        }
        return *this;
    }

    /**
     * @brief 矩阵加法
     * 
     * 加法运算符重载,使得矩阵A、B相加时可以使用A+B的语句.
     */
    Matrix operator+(const Matrix &other) const{
        if(row != other.row || column != other.column){
            std::cerr << "Warning! The two matrices are not in the same dimension!" << std::endl;
            exit(-1);
        }
        Matrix M_sum(row, column);
        for(int i = 0; i < row; i++){
            for(int j = 0; j < column; j++){
                double sum = entry[i][j] + other.entry[i][j];
                M_sum.set_entry(i, j, sum);
            }
        }
        return M_sum;
    }

    /**
     * @brief 矩阵减法
     * 
     * 减法运算符重载,使得矩阵A、B相减时可以使用A-B的语句.
     */
    Matrix operator-(const Matrix &other) const{
        return *this + (-1) * other;
    }

    /**
     * @brief 矩阵数乘
     * 
     * 乘法运算符重载,使得矩阵A和标量c相乘时可以使用A*c的语句.
     */
    Matrix operator*(double scalar) const{
        Matrix M_scalar(row, column);
        for(int i = 0; i < row; i++){
            for(int j = 0; j < column; j++){
                double scalar_entry = scalar * entry[i][j];
                M_scalar.set_entry(i, j, scalar_entry);
            }
        }
        return M_scalar;
    }
    
    /**
     * @brief 矩阵数乘
     * 
     * 使用友元函数,使得矩阵A和标量c相乘时可以使用c*A的语句.
     */
    friend Matrix operator*(double scalar, const Matrix & M)
    {
        return M * scalar;
    }

    /**
     * @brief 矩阵乘法
     * 
     * 乘法运算符重载,使得矩阵A、B相乘时可以使用A*B的语句.
     */
    Matrix operator*(const Matrix &other) const{
        if(column != other.row){
            std::cerr << "Warning! The number of columns in the first matrix is different from the number of rows in the second matrix!" << std::endl;
            exit(-1);
        }
        Matrix M_mult(row, other.column);
        for(int i = 0; i < row; i++){
            for(int j = 0; j < other.column; j++){
                double M_mult_entry = 0.0;
                for(int k = 0; k < column; k++){
                    M_mult_entry += entry[i][k] * other.entry[k][j];
                }
                M_mult.set_entry(i, j, M_mult_entry);
            }
        }
        return M_mult;
    }
};

#endif